{ "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Working on a Windows 10\n", "Python version 3.11.8 | packaged by conda-forge | (main, Feb 16 2024, 20:40:50) [MSC v.1937 64 bit (AMD64)]\n", "Pandas version 2.2.3\n", "bifacial_radiance version 0.5.0b2.dev4+gedb973d.d20250924\n" ] } ], "source": [ "# This information helps with debugging and getting support :)\n", "import sys, platform\n", "import pandas as pd\n", "import bifacial_radiance as br\n", "import numpy as np\n", "print(\"Working on a \", platform.system(), platform.release())\n", "print(\"Python version \", sys.version)\n", "print(\"Pandas version \", pd.__version__)\n", "print(\"bifacial_radiance version \", br.__version__)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 5 - Bifacial Carports and Canopies\n", "\n", "This journal shows how to model a carport or canopy ~ a fixed structure, usually at a high clearance from the ground, with more than one bifacial solar module in the same inclined-plane to create a \"shade\" for the cars/people below.\n", "\n", "We assume that bifacia_radiacne is already installed in yoru computer. This works for bifacial_radiance v.3 release.\n", "\n", "These journal outlines 4 useful uses of bifacial_radiance and some tricks: \n", "\n", "\n", "\n", "This is what we will create:\n", "![Carport Image We will create](../images_wiki/Carport.png)\n", "\n", "### Steps:\n", "\n", "1. Setup of Variables through Making OCT Axis \n", "2. Adding the pillars \n", "3. Analysis of the collector width \n", "4. Mapping the irradiance througout all the Carport \n", "5. Adding a \"Car\" \n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 1. Setup of Variables through Making OCT Axis\n", "\n", "We've done this before a couple times, no new stuff here. \n", "\n", "The magic is that, for doing the carport we see in the figure, we are going to do a 4-up configuration of modules (**numpanels**), and we are going to repeat that 4-UP 7 times (**nMods**)" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Your simulation will be stored in C:\\Users\\cdeline\\Documents\\Python Scripts\\Bifacial_Radiance\\bifacial_radiance\\TEMP\n", "path = C:\\Users\\cdeline\\Documents\\Python Scripts\\Bifacial_Radiance\\bifacial_radiance\\TEMP\n", "Loading albedo, 1 value(s), 0.280 avg\n", "1 nonzero albedo values.\n", "Getting weather file: USA_NJ_McGuire.AFB.724096_TMY3.epw\n", " ... OK!\n", "8760 line in WeatherFile. Assuming this is a standard hourly WeatherFile for the year for purposes of saving Gencumulativesky temporary weather files in EPW folder.\n", "Coercing year to 2021\n", "Saving file EPWs\\metdata_temp.csv, # points: 8760\n", "Calculating Sun position for Metdata that is right-labeled with a delta of -30 mins. i.e. 12 is 11:30 sunpos\n", "\n", "Warning: boolean input `torquetube` passed into makeModule. Starting in v0.4.0 this boolean parameter is deprecated. Use module.addTorquetube() with `visible` parameter instead.\n", "\n", "Module Name: test-module\n", "Module test-module updated in module.json\n", "Pre-existing .rad file objects\\test-module.rad will be overwritten\n", "\n", "Created tutorial_5.oct\n" ] } ], "source": [ "from pathlib import Path\n", "testfolder = str(Path().resolve().parent.parent / 'bifacial_radiance' / 'TEMP')\n", "\n", "print (\"Your simulation will be stored in %s\" % testfolder)\n", "\n", "simulationname = 'tutorial_5'\n", "\n", "# MakeModule Parameters\n", "moduletype='test-module'\n", "numpanels = 4 # Carport will have 4 modules along the y direction (N-S since we are facing it to the south) .\n", "x = 0.95 \n", "y = 1.95\n", "xgap = 0.15 # Leaving 15 centimeters between modules on x direction\n", "ygap = 0.10 # Leaving 10 centimeters between modules on y direction\n", "zgap = 0 # no gap between modules and torquetube.\n", "sensorsy = 10*numpanels # this will give 70 sensors per module.\n", "\n", "# Other default values:\n", "\n", "# TorqueTube Parameters\n", "torquetube = False\n", "\n", "# SceneDict Parameters\n", "gcr = 0.33 # We are only doing 1 row so this doesn't matter\n", "albedo = 0.28 #'concrete' # ground albedo\n", "clearance_height = 4.3 # m \n", "nMods = 7 # six modules length.\n", "nRows = 1 # only 1 row\n", "\n", "azimuth_ang=180 # Facing south\n", "tilt =20 # tilt. \n", "\n", "\n", "## Now let's run the example\n", "\n", "demo = br.RadianceObj(simulationname,path = testfolder)\n", "demo.setGround(albedo)\n", "epwfile = demo.getEPW(40.0583,-74.4057) # NJ lat/lon 40.0583° N, 74.4057\n", "metdata = demo.readWeatherFile(epwfile, coerce_year=2021) \n", "\n", "timestamp = metdata.datetime.index(pd.to_datetime('2021-06-17 13:0:0 -5'))\n", "demo.gendaylit(timestamp) # Use this to simulate only one hour at a time. \n", "\n", "mymodule=demo.makeModule(name=moduletype,x=x,y=y,numpanels = numpanels, xgap=xgap, ygap=ygap, torquetube=torquetube)\n", "\n", "sceneDict = {'tilt':tilt,'pitch': round(gcr/mymodule.sceney,3),'clearance_height':clearance_height,'azimuth':azimuth_ang, 'module_type':moduletype, 'nMods': nMods, 'nRows': nRows} \n", "scene = demo.makeScene(module=mymodule, sceneDict=sceneDict) \n", "octfile = demo.makeOct(demo.getfilelist())\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you view the Oct file at this point, you should see the array of 7 modules, with 4 modules each along the collector widt." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2. Adding the pillars\n", "\n", "We will add 4 pillars at roughly the back and front corners of the structure. Some of the code below is to calculate the positions of where the pillars will be at.\n", "\n", "We are calculating the location with some math geometry" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "! genbox black cuteBox 0.5 0.5 4.966939279485054 | xform -t -0.25 -0.25 0 -t -3.8 -1.8324006105325215 0\n", "\n", "Custom Object Name objects\\Post1.rad\n", "\n", "Custom Object Name objects\\Post2.rad\n", "\n", "Custom Object Name objects\\Post3.rad\n", "\n", "Custom Object Name objects\\Post4.rad\n", "Created tutorial_5.oct\n" ] } ], "source": [ "xright= x*4\n", "xleft= -xright\n", "\n", "y2nd = -(y*numpanels/2)*np.cos(tilt*np.pi/180) + (y)*np.cos(tilt*np.pi/180)\n", "y6th= -(y*numpanels/2)*np.cos(tilt*np.pi/180) + (y*numpanels)*np.cos(tilt*np.pi/180)\n", "z2nd = (y*np.sin(tilt*np.pi/180))+clearance_height\n", "z6th = (y*numpanels)*np.sin(tilt*np.pi/180)+clearance_height\n", "\n", "name='Post1'\n", "text='! genbox black cuteBox 0.5 0.5 {} | xform -t -0.25 -0.25 0 -t {} {} 0'.format(z2nd, xleft, y2nd)\n", "print (text)\n", "customObject = demo.makeCustomObject(name,text)\n", "scene.appendtoScene(customObject=customObject)\n", "\n", "name='Post2'\n", "text='! genbox black cuteBox 0.5 0.5 {} | xform -t -0.25 -0.25 0 -t {} {} 0'.format(z2nd, xright, y2nd)\n", "customObject = demo.makeCustomObject(name,text)\n", "scene.appendtoScene(customObject=customObject)\n", "\n", "name='Post3'\n", "text='! genbox black cuteBox 0.5 0.5 {} | xform -t -0.25 -0.25 0 -t {} {} 0'.format(z6th, xright, y6th)\n", "customObject = demo.makeCustomObject(name,text)\n", "scene.appendtoScene(customObject=customObject)\n", "\n", "name='Post4'\n", "text='! genbox black cuteBox 0.5 0.5 {} | xform -t -0.25 -0.25 0 -t {} {} 0'.format(z6th, xleft, y6th)\n", "customObject = demo.makeCustomObject(name,text)\n", "scene.appendtoScene(customObject=customObject)\n", "\n", "octfile = demo.makeOct() # makeOct combines all of the ground, sky and object files into a .oct file." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### View the geometry with the posts on :\n", "\n", "***rvu -vf views\\front.vp -e .01 -pe 0.4 -vp 3.5 -20 22 tutorial_5.oct***\n", "\n", "-pe sets the exposure levels, and -vp sets the view point so the carport is centered (at least on my screen. you can play with the values). It should look like this:\n", "\n", "![Carpport with posts](../images_wiki/Carport.png)\n", "\n", "The post should be coindient with the corners of the array on the high-end of the carport, and on the low end of the carport they should be between the lowest module and the next one. Cute! \n", "\n" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "## Comment the ! line below to run rvu from the Jupyter notebook instead of your terminal.\n", "## Simulation will stop until you close the rvu window\n", "\n", "#!rvu -vf views\\front.vp -e .01 -pe 0.4 -vp 3.5 -20 22 tutorial_5.oct\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 3. Analysis of the collector width\n", "\n", "Now let's do some analysis along the slope of the modules. Each result file will contain irradiance for the 4 modules that make up the slope of the carport. You can select which \"module\" along the row you sample too.\n", "\n", "We are also increasign the number of points sampled accross the collector width, with the variable **sensorsy** passed to **moduleanalysis**" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Linescan in process: tutorial_5Mod1_Row1_Module1_Front\n", "Linescan in process: tutorial_5Mod1_Row1_Module1_Back\n", "Saved: results\\irr_tutorial_5Mod1_Row1_Module1.csv\n", "Annual bifacial ratio average: 0.179\n", "\n" ] } ], "source": [ "analysis = br.AnalysisObj(octfile, demo.name) \n", "modWanted = 1\n", "rowWanted = 1\n", "frontscan, backscan = analysis.moduleAnalysis(scene, modWanted=modWanted, rowWanted=rowWanted, sensorsy=sensorsy)\n", "\n", "analysis.analysis(octfile, simulationname+\"Mod1\", frontscan, backscan) \n", "print('Annual bifacial ratio average: %0.3f' %( sum(analysis.Wm2Back) / sum(analysis.Wm2Front) ) ) \n", "print(\"\")\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "This is the module analysis and an image of the results file\n", "![This is the module analysed.](../images_wiki/Carport_analysis.PNG)\n", "\n", "(Notice in the image above the module name we originally used in this tutorial was \"Prism Solar\" and not \"test-module\". Otherwise your results should look the same.)\n", "\n", "\n", "You can repeat the analysis for any other module in the row:\n", "\n", "
\n", "Notice we are passing a CUSTOM simulation name so the results are generated in separate csv files.\n", "
\n" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Linescan in process: tutorial_5Mod2_Row1_Module2_Front\n", "Linescan in process: tutorial_5Mod2_Row1_Module2_Back\n", "Saved: results\\irr_tutorial_5Mod2_Row1_Module2.csv\n", "Annual bifacial ratio average: 0.175\n", "Linescan in process: tutorial_5Mod3_Row1_Module3_Front\n", "Linescan in process: tutorial_5Mod3_Row1_Module3_Back\n", "Saved: results\\irr_tutorial_5Mod3_Row1_Module3.csv\n", "Annual bifacial ratio average: 0.173\n" ] } ], "source": [ "modWanted = 2\n", "rowWanted = 1\n", "frontscan, backscan = analysis.moduleAnalysis(scene, modWanted=modWanted, rowWanted=rowWanted, sensorsy=sensorsy)\n", "\n", "analysis.analysis(octfile, simulationname+\"Mod2\", frontscan, backscan) \n", "print('Annual bifacial ratio average: %0.3f' %( sum(analysis.Wm2Back) / sum(analysis.Wm2Front)) )\n", "\n", "\n", "modWanted = 3\n", "rowWanted = 1\n", "frontscan, backscan = analysis.moduleAnalysis(scene, modWanted=modWanted, rowWanted=rowWanted, sensorsy=sensorsy)\n", " \n", "analysis.analysis(octfile, simulationname+\"Mod3\", frontscan, backscan) \n", "print('Annual bifacial ratio average: %0.3f' %( sum(analysis.Wm2Back) / sum(analysis.Wm2Front) ) )" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 4. Mapping the irradiance througout all the Carport (\"Hack\" the sensors) \n", "\n", "You can \"hack\" the sensors starting locations to obtain an irradinace distribution map. This is easier when the modules are facing South, or East/West. Below is an example, you'll have to repeat over all the modules/ all the surface area with as much resolution as you have patience to see edge-effects." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Linescan in process: tutorial_5Mod3_point2_Row1_Module3_Front\n", "Linescan in process: tutorial_5Mod3_point2_Row1_Module3_Back\n", "Saved: results\\irr_tutorial_5Mod3_point2_Row1_Module3.csv\n", "Annual bifacial ratio average: 0.173\n" ] } ], "source": [ "# HACK Frontscan and Backscan\n", "frontscan['xstart']=-1.2\n", " \n", "analysis.analysis(octfile, simulationname+\"Mod3_point2\", frontscan, backscan) \n", "print('Annual bifacial ratio average: %0.3f' %( sum(analysis.Wm2Back) / sum(analysis.Wm2Front) ) )\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "The printed Annual bifacial ratio does not include cleaning of the sensors for the material. Some of the sensros might fall in the spacing between the modules (ygaps) or in the structures added, torquetubes, etc. For a real bifacial ratio gain, use the load and clean functions in load.py. \n", "\n", "(This process might be automated in a future release TBD)\n", "
\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 5. Adding a \"Car\"\n", "\n", "Add a surface (just like we added the pillars) with a specific reflectivity to represent a car. If you are doing hourly simulation you can compare how much the irradiance increases with and without the car, and if you keep track of your parking lot comings/goings this could make an interesting toy-problem: how much are your employees contributing to your rear irradiance production? " ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "Custom Object Name objects\\Car_1.rad\n", "Created tutorial_5.oct\n" ] } ], "source": [ "name='Car_1'\n", "carpositionx=-2\n", "carpositiony=-1\n", "text='! genbox white_EPDM HondaFit 1.6 4.5 1.5 | xform -t -0.8 -2.25 0 -t {} {} 0'.format(carpositionx, carpositiony)\n", "customObject = demo.makeCustomObject(name,text)\n", "scene.appendtoScene(customObject=customObject)\n", "\n", "octfile = demo.makeOct(demo.getfilelist()) # run makeOct to combine the ground, sky and all new scene object files" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Viewing with:\n", "## rvu -vf views\\front.vp -e .01 -pe 0.019 -vp 1.5 -14 15 tutorial_5.oct\n", "\n", "\n", "![Behold the Honda-fit sized cube](../images_wiki/Carport_with_car.PNG)" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "\n", "## Comment the ! line below to run rvu from the Jupyter notebook instead of your terminal.\n", "## Simulation will stop until you close the rvu window\n", "\n", "!rvu -vf views\\front.vp -e .01 -pe 0.019 -vp 1.5 -14 15 tutorial_5.oct\n" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.13" } }, "nbformat": 4, "nbformat_minor": 4 }